using System;
using System.Drawing;
using System.Windows.Forms;
using SpellCore;

namespace SpellerExampleClient
{
    public class RichTextBoxWithSpelling : RichTextBoxWithSpecialUnderlines
    {
        private int _positionNeedingChecking = 0;
        //private bool _inSpellingContextMenu = false;

        protected override void OnMouseDown(System.Windows.Forms.MouseEventArgs e)
        {
            base.OnMouseDown(e);
            
            if(e.Button == System.Windows.Forms.MouseButtons.Right)
            {
                ShowSpellingContextMenu(e.Location);
            }
            /*else
            {
                SpellCheckPositionNeedingChecking();
            }*/
        }

        internal void ShowSpellingContextMenu(System.Drawing.Point location)
        {
            //_inSpellingContextMenu = true;
            Select(GetCharIndexFromPosition(location), 0);

            int wordBeingEditedStartIndex;
            string wordBeingEdited = GetWordFromPosition(Text, SelectionStart, out wordBeingEditedStartIndex);
            CheckingResult result = CheckAndUpdateDisplayForWord(wordBeingEdited, wordBeingEditedStartIndex);
            ContextMenuStrip = new ContextMenuStrip();
            
            if(result != CheckingResult.Good)
            {
                foreach(SimilarityCheckResult suggestion in _spellSession.GetSuggestions(wordBeingEdited))
                {
                    ContextMenuStrip.Items.Add(suggestion.Word);
                }
            }
            Font contextMenuFont = new Font("Tahoma",12);
            ContextMenuStrip.Font = contextMenuFont;
            ContextMenuStrip.ItemClicked += new ToolStripItemClickedEventHandler(_context_ItemClicked);
            //_inSpellingContextMenu = false;
        }

        void _context_ItemClicked(object sender, ToolStripItemClickedEventArgs e)
        {
            int wordBeingEditedStartIndex;
            string wordBeingEdited = GetWordFromPosition(Text, SelectionStart, out wordBeingEditedStartIndex);
            Select(wordBeingEditedStartIndex, wordBeingEdited.Length);
            SelectedText = e.ClickedItem.Text;
        }

        protected override void OnTextChanged(EventArgs e)
        {
            base.OnTextChanged(e);
            SpellCheckPositionNeedingChecking();
        }

        protected override void OnLostFocus(EventArgs e)
        {
            base.OnLostFocus(e);
            SpellCheckPositionNeedingChecking();
        }

        protected override void OnSelectionChanged(EventArgs e)
        {
            base.OnSelectionChanged(e);
            if (SelectionLength == 0)
            {
                SpellCheckPositionNeedingChecking();
            }
        }

        private AggregatedSpellSession _spellSession;
        private string _writingSystemId;

        public string WritingSystemId
        {
            get { return _writingSystemId; }
            set
            {
                _writingSystemId = value;
                if (!DesignMode)
                {
                    _spellSession = new AggregatedSpellSession(_writingSystemId);
                }
            }
        }

        internal AggregatedSpellSession AggregateSpellingSession
        {
            get { return _spellSession; }
            set { _spellSession = value; }
        }

        bool _inSpellCheckPositionNeedingChecking = false;
        private void SpellCheckPositionNeedingChecking()
        {
            if (_inSpellCheckPositionNeedingChecking) // no reentry!
            {
                return;
            }

            if (_spellSession == null || String.IsNullOrEmpty(Text))
            {
                return;
            }
            _inSpellCheckPositionNeedingChecking = true;


            // don't do anything if the position needing checking is in the same word as our current selection
            int wordToCheckStartIndex, wordBeingEditedStartIndex;
            string wordToCheck = GetWordFromPosition(Text, _positionNeedingChecking, out wordToCheckStartIndex);
            //SelectionStart == current cursor position
            string wordBeingEdited = GetWordFromPosition(Text, SelectionStart, out wordBeingEditedStartIndex);

            int originalSelectionStart = SelectionStart;
            int originalSelectionLength = SelectionLength;

            if (SelectionLength == 0 && (SelectionStart > 0 && Text[SelectionStart - 1] == ' '))
            {
                Select(SelectionStart - 1, 1);
                SelectionUnderlineStyle = UnderlineStyle.None;
                Select(originalSelectionStart, originalSelectionLength);
                SelectionUnderlineStyle = UnderlineStyle.None;
            }
            if (wordBeingEdited.Length > 0)
            {
                bool wordBeingEditedIsMisspelled = false;
                Select(wordBeingEditedStartIndex, 1);
                if (SelectionUnderlineStyle == UnderlineStyle.Wave)  
                {
                    wordBeingEditedIsMisspelled = true;
                }
                else
                {
                    Select(wordBeingEditedStartIndex + wordBeingEdited.Length - 1, 1);
                    if (SelectionUnderlineStyle == UnderlineStyle.Wave)
                    {
                        wordBeingEditedIsMisspelled = true;
                    }
                }

                if (wordBeingEditedIsMisspelled)
                {
                    CheckAndUpdateDisplayForWord(wordBeingEdited, wordBeingEditedStartIndex);
                }
            }

            if (wordBeingEditedStartIndex != wordToCheckStartIndex)
            {
                CheckAndUpdateDisplayForWord(wordToCheck, wordToCheckStartIndex);
            }

            Select(originalSelectionStart, originalSelectionLength);
            
            //SelectionUnderlineStyle = UnderlineStyle.None;

             _positionNeedingChecking = SelectionStart;
            _inSpellCheckPositionNeedingChecking = false;
        }

        private CheckingResult CheckAndUpdateDisplayForWord(string wordToCheck, int wordToCheckStartIndex)
        {
            int originalSelectionStart = SelectionStart;
            int originalSelectionLength = SelectionLength;

            Select(wordToCheckStartIndex, wordToCheck.Length);
            CheckingResult result = _spellSession.GetWordStatus(wordToCheck);
            switch (result)
            {
                case CheckingResult.Unknown:
                case CheckingResult.StructurallyUnsound:
                case CheckingResult.Verify:
                case CheckingResult.Bad:
                    SelectionUnderlineStyle = UnderlineStyle.Wave;
                    SelectionUnderlineColor = UnderlineColor.Red;
                    break;
                case CheckingResult.Good:
                    SelectionUnderlineStyle = UnderlineStyle.None;
                    break;
                default:
                    break;
            }
            Select(originalSelectionStart, originalSelectionLength);
            //SelectionUnderlineStyle = UnderlineStyle.None;
            return result;
        }

        private static string GetWordFromPosition(string text, int position, out int wordStartIndex)
        {
            int start;
            if(position > text.Length)
            {
                position = text.Length;
            }
            if (position == 0)
            {
                start = -1;
            }
            else
            {
                start = text.LastIndexOfAny(new char[] {' ', '\t', '\n'}, position - 1);
            }
            ++start;
            wordStartIndex = start; // cursor happens after
            
            if(start > text.Length)
            {
                start = text.Length;
            }
            int end = text.IndexOfAny(new char[] {' ', '\t', '\n'}, position);
            if(end == -1)
            {
                end = text.Length;
            }
            string word = text.Substring(start, end - start);
            return word;
        }
    }
}